# -*- coding:utf-8 -*-
import requests
import json
from Common.com_func import json_path_value, log
from Common.custom_exception import CoreFieldException
import re, copy
from Util.decorator_tools import retry_request
from Config import pro_config
from Config.pro_config import http_timeout
from Util.tool import capture_str_variable


class VerifyInterface(object):
    """
    【 验 证 接 口 】
     1.转换 参数 格式类型（ 将 mongo 中的 str 类型 转成 需要的类型 ）
        若转换失败，记录'测试结果：test_result'<error:'请求参数'或'请求头文件'格式有误>
        若转换成功，继续
     2.发送请求，验证response响应
      （1）无响应：记录'测试结果：test_result' < fail:测试接口无响应 >
      （2）有相应（ http != 200 ）：记录'测试结果：test_result' < fail:测试接口错误,http_code<500>,原因解析(Internal Server Error)" >
      （3）有响应（ http == 200 ）：继续
     3.捕获 HTTP 状态码 和 响应时间
     4.记录 '响应信息：response_info' 字段值
     5.获取'实际的响应字段列表、键值字典'
     6.验证'待比较的关键字段名'列表
      （1）获取'实际的关键字段值'列表
      （2）比较'关键字段值'列表
         记录 '响应字段列表比较结果：result_core_field_value'（ pass、fail）
     7.若'验证模式 = 2'，则还需要验证'待比较的响应字段列表'
         记录 '响应字段列表比较结果：result_field_name_list'（ pass、fail ）
     8.记录 '测试结果：test_result' 字段值
      （1）success:通过
      （2）fail:关键字段验证失败
      （3）fail:关键字段验证失败,响应字段列表验证失败
      （4）fail:关键字段验证通过,响应字段列表验证失败
      （5）fail:关键字段验证失败,响应字段列表验证通过
     9.retrun: 待更新字典

       < 验 证 接 口 test_result >
        01.success:测试通过
        02.fail:验证字段'xxxx'不存在
        03.fail:关键字段验证失败
        04.fail:关键字段验证失败,响应字段列表验证失败
        05.fail:关键字段验证通过,响应字段列表验证失败
        06.fail:关键字段验证失败,响应字段列表验证通过
        07.fail:测试接口错误,http_code<500>,原因解析(Internal Server Error)
        08.fail:响应信息格式不是JSON
        09.fail:测试接口调用异常
        10.error:'请求参数'或'请求头文件'格式有误
        11.error:上传的文件不存在
        12.error:上传的文件不能为空
        13.error:请求头不能为空
        14.error:请求头中的'Content-Type'类型暂不支持
        15.error:请求头中的'Content-Type'字段不存在
        16.error:上传文件接口的请求头不能包含'Content-Type'
        17.error:验证字段索引超出取值范围
        18.error:验证字段条件配置有误
        19.error:关联用例未全部上线


        01.fail:依赖接口调用异常
        02.fail:依赖接口无响应
        03.fail:依赖接口错误,http_code<500>,原因解析(Internal Server Error)
        04.error:依赖接口'请求参数'或'请求头文件'格式有误
        05.error:依赖接口上传的文件不存在
        06.error:依赖接口上传的文件不能为空
        07.error:依赖接口请求头不能为空
        08.error:依赖接口请求头中的'Content-Type'类型暂不支持
        09.error:依赖接口请求头中的'Content-Type'字段不存在
        10.error:依赖字段值没有全部获取到(all)
        11.error:依赖字段名配置有遗漏(all)
        12.error:依赖接口不存在(all)
        13.error:依赖接口响应信息格式有误(all)
    """

    def __init__(self, host, case, case_type="unrelation", relation_dict=None, all_relation_online=True):
        # 请求相关
        self.host = host
        self.interface_name = case.get("interface_name")
        self.interface_url = case.get("interface_url")
        self.request_method = case.get("request_method")
        self.request_header = case.get("request_header")
        self.request_params = case.get("request_params")
        self.request_body = case.get("request_body")
        # 验证模式
        self.verify_mode = case.get("verify_mode")
        # 依赖数据
        self.depend_interface_list = case.get("depend_interface_list")
        self.depend_field_name_list = case.get("depend_field_name_list")
        # 响应信息
        self.response_info = ""
        # 关键字段
        self.compare_core_field_name_list = case.get("compare_core_field_name_list")
        self.expect_core_field_value_list = case.get("expect_core_field_value_list")
        self.actual_core_field_value_list = []
        self.result_core_field_value = ""
        # 响应字段名列表
        self.expect_field_name_list = case.get("expect_field_name_list")
        self.actual_field_name_list = []
        self.result_field_name_list = ""

        # 实际响应信息中的递归捕获的 所有 field_value 字典  ->  { "name": ["a", "b"], "age": [20] }
        self.actual_field_dict = {}

        # 上下游关联：上游接口名称、下游关联字段名列表、下游关联字段值列表
        self.upstream_relation_case = case.get("upstream_relation_case")
        self.downstream_relation_field_name_list = case.get("downstream_relation_field_name_list")
        self.downstream_relation_field_value_list = []

        self.case_type = case_type  # 关联接口标记
        self.relation_dict = relation_dict  # 当前获取的整体关联字典（所有已执行的上游接口的关联字段）
        self.all_relation_online = all_relation_online  # 当前用例的关联接口是否全部'上线'

        self.test_result = ""  # 测试结果
        self.update_result_dict = {}  # 待数据库更新的字典

        self.httpCode_i = -1        # HTTP状态码 在'待比较关键字段列表'中的索引
        self.httpCode_expect = -1   # HTTP状态码 在'期望关键字段值列表'中的值
        self.httpCode_actual = -1   # HTTP状态码 实际值

        self.httpRT_i = -1       # HTTP响应时间 在'待比较关键字段列表'中的索引
        self.httpRT_expect = -1  # HTTP响应时间 在'期望关键字段值列表'中的值
        self.httpRT_actual = -1  # HTTP响应时间 实际值

    def get_http_code_rt_index(self):
        """
         获取 待比较 HTTP状态码 和 响应时间 的索引位置
          <判断> 若 '待比较关键字段列表'含有'httpCode'字段：
            1.获取'待比较关键字段列表'中'httpCode'的索引位置
            2.获取'期望关键字段值列表'中'httpCode'对应的值 (通过该索引)
            3.在 '待比较关键字段列表、期望关键字段值列表'中 删除'httpCode'元素（为了不影响后续与响应JSON做比较）

             < 注：后续的验证逻辑 在 verify_core_field_list 函数中统一处理 >
            （ 根据索引，在'待比较关键字段列表、期望关键字段值列表、实际关键字段值列表'插入相应的数据 ）

            < 原 因 > 因为这两个待比较的字段值 不是从响应体中获取的，比较逻辑不一样
        """
        if "httpCode" in self.compare_core_field_name_list:
            self.httpCode_i = self.compare_core_field_name_list.index("httpCode")
            self.httpCode_expect = self.expect_core_field_value_list[self.httpCode_i]
        if "httpRT" in self.compare_core_field_name_list:
            self.httpRT_i = self.compare_core_field_name_list.index("httpRT")
            self.httpRT_expect = int(self.expect_core_field_value_list[self.httpRT_i])

        # 只有"httpCode"的情况
        if self.httpCode_i != -1 and self.httpRT_i == -1:
            del self.compare_core_field_name_list[self.httpCode_i]
            del self.expect_core_field_value_list[self.httpCode_i]
        # 只有"httpRT"的情况
        if self.httpCode_i == -1 and self.httpRT_i != -1:
            del self.compare_core_field_name_list[self.httpRT_i]
            del self.expect_core_field_value_list[self.httpRT_i]
        # "httpCode、httpRT"都存在的情况
        if self.httpCode_i != -1 and self.httpRT_i != -1:
            # 比较两个索引大小，先删除索引大的，再删除索引小的（ 确保删除的位置不会错乱 ）
            if self.httpCode_i < self.httpRT_i:
                del self.compare_core_field_name_list[self.httpRT_i]
                del self.expect_core_field_value_list[self.httpRT_i]
                del self.compare_core_field_name_list[self.httpCode_i]
                del self.expect_core_field_value_list[self.httpCode_i]
            else:
                del self.compare_core_field_name_list[self.httpCode_i]
                del self.expect_core_field_value_list[self.httpCode_i]
                del self.compare_core_field_name_list[self.httpRT_i]
                del self.expect_core_field_value_list[self.httpRT_i]

    def verify(self):
        """
            执行接口测试（ 关联接口、非关联接口 ）

            < 执行逻辑 >
            '关联接口'  : 替换'上游接口'获取的关联字段 > 执行 > 捕获关联字段
            '非关联接口' : 执行

            < 关联接口 主流程 >
            1.判断是否需要替换关联字段
                若需要：替换 关联字段值、判断 关联字段是否全部替换完成
            2.执行测试
            3.若当前为'关联接口'，则需要捕获关联字段
        """
        # 判断 当前是否为关联接口
        if self.case_type == "relation":
            if self.all_relation_online:
                # 判断是否需要替换关联字段 ( 若存在上游关联用例，则需要 )
                if self.upstream_relation_case:
                    # 替换 关联字段值
                    for key, value in self.relation_dict.items():
                        self.interface_url = self.interface_url.replace("[" + key + "]", value)
                        self.request_header = self.request_header.replace("[" + key + "]", value)
                        self.request_params = self.request_params.replace("[" + key + "]", value)
                        self.request_body = self.request_body.replace("[" + key + "]", value)
                    # 判断 关联字段是否全部替换完成
                    no_replace_list = []
                    no_replace_list.extend(capture_str_variable(str=self.interface_url, v_tag="["))
                    no_replace_list.extend(capture_str_variable(str=self.request_header, v_tag="["))
                    no_replace_list.extend(capture_str_variable(str=self.request_params, v_tag="["))
                    no_replace_list.extend(capture_str_variable(str=self.request_body, v_tag="["))
                    if no_replace_list:
                        self.test_result = f"error:关联字段'{no_replace_list[0]}'未获取到"
            else:
                self.test_result = "error:关联用例未全部上线"
        if not self.test_result:
            # 1.转换 参数 格式类型
            error_msg, file, self.request_params, self.request_header, self.request_body = \
                self.transform_params_format(request_params=self.request_params, request_header=self.request_header,
                                             request_body=self.request_body)
            if error_msg:
                self.test_result = "error:" + error_msg
            else:
                # 2.发送请求，验证response响应
                try:
                    response, session = \
                        self.send_request(request_method=self.request_method, interface_url=self.host+self.interface_url,
                                          request_params=self.request_params,request_header=self.request_header,
                                          request_body=self.request_body, file=file)
                except Exception:
                    self.test_result = "fail:测试接口调用异常"
                else:
                    if response == 31500:
                        self.test_result = "fail:测试接口无响应"
                    else:
                        # 获取 HTTP 状态码 和 响应时间（毫秒）
                        self.httpCode_actual = response.status_code
                        self.httpRT_actual = int(response.elapsed.total_seconds() * 1000)
                        if response.status_code not in [200, 302]:
                            msg = re.search(r'<title>(.*?)</title>', response.text)
                            try:
                                reason = msg.group(1)
                                self.test_result = f"fail:测试接口错误,http_code<{str(response.status_code)}>原因解析({reason})"
                            except Exception:
                                self.test_result = f"fail:测试接口错误,http_code<{str(response.status_code)}>"
                        else:
                            # 3.获取 待比较 HTTP状态码 和 响应时间 的索引位置
                            self.get_http_code_rt_index()
                            # 4.记录'响应信息：response_info'字段值
                            self.response_info = response.text
                            if not self.response_info.startswith("{"):
                                self.test_result = "fail:响应信息格式不是JSON"
                            else:
                                # 5.获取'实际的响应字段列表、键值字典'
                                self.get_response_field_list_and_dict()

                                # 6.验证'待比较的关键字段'列表
                                error_msg = self.verify_core_field_list()
                                if error_msg:
                                    self.test_result = error_msg
                                else:
                                    # 7.若'验证模式 = 2'，则验证'待比较的响应字段列表'
                                    self.verify_mode == 2 and self.verify_response_field_name_list()
                                    # 8.记录 '测试结果：test_result' 字段值
                                    self.get_test_result()
                                    # 转换 响应信息（ 防止中文乱码 ） 先转成字典 再转成字符串
                                    self.response_info = str(json.loads(self.response_info))
                                    # 若当前为'关联接口'，则需要捕获关联字段
                                    if self.case_type == "relation":
                                        # 将捕获的关联字段 存入 '当前获取的整体关联字典'
                                        self.capture_relation_field(json.loads(response.text))
                                        # 获取当前用例的 下游关联字段值列表
                                        self.get_relation_field_value()
        # 获取 待数据库更新的字典
        self.get_mongo_update_result_dict()
        if self.case_type == "relation":
            return self.update_result_dict, self.relation_dict
        else:
            return self.update_result_dict

    def capture_relation_field(self, response_dict):
        """
        捕获关联字段
        :param response_dict:  响应信息 字典
        :param depend_field_body_list: 待捕获的 响应体中的字段列表
            举例：
            捕获第1个值 -> uid
            捕获第3个值 -> uid::3
            捕获最后1个值 -> uid::0
        """
        # 方式一：递归捕获'依赖字段值'（ 暂时不用 ）
        # self.recur_capture_relation(response_dict)

        # 方式二：jsonpath捕获
        error_msg = ""
        for field in self.downstream_relation_field_name_list:  # 捕获'关联字段值'
            tmp = field.split("::")
            json_path = tmp[0]
            field_index = len(tmp) == 2 and int(tmp[1]) - 1 or 0
            value = json_path_value(data_dict=response_dict, json_path=json_path, field_index=field_index)
            if "超出了最大范围" not in value and "json字段取值失败" not in value:
                self.relation_dict[field] = value
        return error_msg

    def recur_capture_relation(self, response_dict):
        """
        递归 捕获 关联字段  ( 暂时不用，由jsonpath取代 )
        1.捕获 相应信息中的关联字段值
        2.若存在相同字段名的，取最后一个（默认）
        """
        if isinstance(response_dict, dict):
            for key, value in response_dict.items():
                if isinstance(value, list):
                    for ele in value:
                        self.recur_capture_relation(ele)
                elif isinstance(value, dict):
                    self.recur_capture_relation(value)
                else:
                    if key in self.downstream_relation_field_name_list:
                        self.relation_dict[key] = value

    def get_relation_field_value(self):
        """
            获取 当前用例的 下游关联字段值列表
            1.先从'当前获取的整体关联字典'中过滤出'当前用例所需的关联字段'
            2.再获取对应的关联字段值
        """
        self.downstream_relation_field_value_list = \
            list(map(lambda x: x[1], filter(lambda k_v: k_v[0] in self.downstream_relation_field_name_list,
                                            self.relation_dict.items())))


    @staticmethod
    def transform_params_format(request_params, request_header, request_body):
        """
        转换 参数 格式类型（ 将 mongo 中的 str 类型 转成 需要的类型 ）
        1.'请求参数、请求头文件'格式转换：str -> dict
             ?key=value&key=value' -> {"key": "value"}

        2.'请求正文体'相关规则判断
          <判断>当前是否为上传文件的请求 < 是否存在'file'字段 >
        （1）若是：-- 上传文件请求规则
              将该字段的键值对提取出来存放入 file字典中
              <判断>请求头中不能包含 Content-Type 字段
        （2）若不是 -- 非上传文件请求规则
             1）判断'请求头'中的'Content-Type'
                请求头是否存在
                请求头中的'Content-Type'是否存在
                请求头中的'Content-Type'是否为支持的类型
            2）若 Content-Type = application/x-www-form-urlencoded，
               入参格式1：字符串 '{"name": "测试", "age": 30}'  出参格式：dict字典
               入参格式2：字符串  name=测试&age=30  出参格式：字符串 ( 不变 )
            3）若 Content-Type = application/json
               入参格式：字符串 '{"name": "测试", "age": 30}'  出参格式：dict字典

         < files 字典的三种情况 >
          files = {}
          files = {"file": ""}
          files = {'file': open(file_path, 'rb')}

        < 当前仅支持 请求头的 Content-Type 的类型 >
        （1）application/json
        （2）application/x-www-form-urlencoded

        【 入参 ：出参 格式类型 】
           1.请求参数  ->   字符串：字典
           2.请求头文件 ->  json字符串：字典
           3.请求正文体：
            （1）上传文件 -> 无所谓
            （2）json格式入参
                  '{"name":"测试","age":30}' -> 字符串：字典
            （3）form格式入参：
                  '{"name":"测试","age":30}' ->  字符串：字典
                  name=测试&age=30  ->  字符串：字符串 ( 不变 )
           4.文件 -> 从正文体中 提取 file 字典

        """
        error_msg = None
        files = {}
        try:
            # 1.'请求头文件、请求参数'格式转换：若非空，则 str -> dict
            request_header = request_header and eval(request_header) or {}
            if request_params:  # '?key=value&key=value' -> {"key": "value"}
                params = copy.copy(request_params)
                params = params[1:]
                params = params.split("&")
                request_params = {}
                for each in params:
                    each_list = each.split("=")
                    request_params[each_list[0]] = each_list[1]
            else:
                request_params = {}

            # 2.'请求正文体'相关规则判断
            if request_body:
                # 判断是否为 上传文件请求
                if request_body.startswith("{") and request_body.endswith("}") and "\"file\":" in request_body:
                    request_body = eval(request_body)
                    if "Content-Type" in request_header.keys() or "content-type" in request_header.keys():
                        error_msg = "上传文件接口的请求头不能包含'Content-Type'"
                    else:
                        files["file"] = request_body.get("file")
                        del request_body["file"]
                        if files["file"]:
                            try:
                                files["file"] = open(files["file"], 'rb')
                            except Exception as e:
                                log.error("\n===============\n" + str(e) + "\n================\n")
                                error_msg = "上传的文件不存在"
                        else:
                            error_msg = "上传的文件不能为空"
                else:
                    if not request_header:
                        error_msg = "请求头不能为空"
                    elif not request_header.get("Content-Type", "") and not request_header.get("content-type", ""):
                        error_msg = "请求头中的'Content-Type'字段不存在"
                    elif request_header.get("Content-Type", "").lower() not in pro_config.get_content_type_list() and \
                            request_header.get("content-type", "").lower() not in pro_config.get_content_type_list():
                        error_msg = "请求头中的'Content-Type'类型暂不支持"
                    else:
                        if request_body.startswith("{") and request_body.endswith("}"):
                            request_body = eval(request_body)  # request_body = json.loads(request_body)
        except Exception as e:
            # 捕获 配置错误 的接口 保存入测试结果，< 忽略 日志中的 invalid syntax  >
            error_msg = "'请求参数、请求头文件、请求正文体'格式有误"
            log.error("\n===============\n" + str(e) + " -> " + error_msg + "\n================\n")
        return error_msg, files, request_params, request_header, request_body

    # try_func = retry_request(try_limit=3,interval_time=1,send_dd=True)
    # send_request = try_func(send_request)
    @staticmethod
    @retry_request(try_limit=3, interval_time=1, send_dd=True)
    def send_request(request_method, interface_url, request_params, request_header, request_body, file):
        """
        【 发 送 请 求 】
        :param request_method:
        :param interface_url:
        :param request_params:
        :param request_header:
        :param request_body
        :param file:  上传文件接口
        :return:

            【 注 意 事 项 】
            1."POST、PUT、DELETE"请求，必须要包含有 'Content-Type' 字段
            2."Content-Type"：目前支持的类型
            （1）application/json
            （2）application/x-www-form-urlencoded

            【 备 注 】
            1.请求默认超时时间，设置 5 秒
            2.失败重试次数，设置 3 次 （每次间隔 1 秒）
            3.一个请求最长用时：5 * 3 + 2 = 17 秒

            # 禁止重定向: allow_redirects=False
        """
        session = requests.session()
        if request_method == "GET":
            res_info = session.get(url=interface_url, params=request_params, headers=request_header,
                                   timeout=http_timeout, allow_redirects=False)
        elif request_method in ["POST", "PUT", "DELETE"]:
            if file:
                res_info = session.post(url=interface_url, params=request_params, data=request_body, files=file,
                                        headers=request_header, timeout=http_timeout)
            else:
                if "json" in request_header.get("Content-Type"):
                    res_info = session.post(url=interface_url, params=request_params, json=request_body,
                                            headers=request_header, timeout=http_timeout, allow_redirects=False)
                else:
                    res_info = session.post(url=interface_url, params=request_params, data=request_body,
                                            headers=request_header, timeout=http_timeout, allow_redirects=False)
        # elif request_method == "PUT":
        #     res_info = session.put(url=interface_url, params=request_params, data=request_body, headers=request_header,
        #                            timeout=http_timeout, allow_redirects=False)
        # else:  # DELETE
        #     res_info = session.delete(url=interface_url, params=request_params, data=request_body,
        #                               headers=request_header, timeout=http_timeout, allow_redirects=False)
        return res_info, session

    def get_response_field_list_and_dict(self):
        """
        获取
        1.响应信息中的所有 field 列表    （ self.actual_field_name_list ）（去重）
        2.响应信息中的 field_value 字典 （ self.actual_field_dict ）
        :return:
        """
        self.recur_caputure_params(self.response_info)
        self.actual_field_name_list = list(set(self.actual_field_name_list))

    def recur_caputure_params(self, response_info):
        """
        递归 捕获 响应体中的所有字段和值
        1.响应信息中的所有 field 列表    （ self.actual_field_name_list ）
        2.响应信息中的 field_value 字典 （ self.actual_field_dict ）
        （1）若 value 为 dict 类型，则该 field_value 不获取
        （2）若 value 为 list 类型，则该 field_value 不获取
        （3）若 存在相同的 field 名称， 则获取所有对应的值存入列表中 -> { "name": ["a", "b"], "age": [20] }

          包含的场景：{[],[]}、{{},{}}、{[{}],[]}、{{[{[]}]}}
          暂不考虑的场景：{[[]]} 列表套列表 当前返回 ->  'response_list': [[1, 'str', False]]
        """
        # 若是字符串类型，且能被转换成字典
        if isinstance(response_info, str) and response_info.startswith("{"):  # 场景：仅第一次会判断
            response_info_dict = json.loads(response_info)
            self.recur_caputure_params(response_info_dict)
        else:  # isinstance(response_info, dict):
            for field, value in response_info.items():
                self.actual_field_name_list.append(field)  # 捕获 field 列表
                if isinstance(value, list):
                    for ele in value:
                        if isinstance(ele, dict):
                            self.recur_caputure_params(ele)
                        else:
                            self.actual_field_dict[field] = self.actual_field_dict.get(field, []) + [ele]
                elif isinstance(value, dict):
                    self.recur_caputure_params(value)
                else:  # str、int、bool
                    self.actual_field_dict[field] = self.actual_field_dict.get(field, []) + [value]

    def verify_core_field_list(self):
        """
            【 验 证 关 键 字 段 】  < 参考 调试模块：debug_field_verify.py >
            1.获取'关键字段值'列表
            2.在'待比较关键字段列表、期望关键字段值列表、实际关键字段值列表'插入httpCode、httpRT相关数据（由于之前移除的缘故）
            3.比较'关键字段值'列表
            （1）关于'httpRT'关键字的比较方式：实际毫秒数 <= 期望毫秒数，则 pass
            （2）其余关键字的比较方式：按照匹配模式进行比较
        """
        error_msg = self.get_core_field_value()
        if not error_msg:
            self.inster_http_code_rt_data()
            self.verify_with_condition(error_msg)
        return error_msg

    def get_core_field_value(self):
        """
            获取关键字段值列表
            1.获取前：
                期望核心字段名列表 compare_core_field_name_list -> ["name::3::S", "age::2"]
                实际核心字段值列表 actual_core_field_value_list = []
            2.获取后：
                期望核心字段名列表 compare_core_field_name_list -> ["name::S", "age"]
                实际核心字段值列表 actual_core_field_value_list = ["Ronaldo", 30]
        """
        error_msg = ""
        for i, field in enumerate(self.compare_core_field_name_list):
            tmp = field.split("::")
            field_name = tmp[0]  # 关键字段名
            field_value_list = self.actual_field_dict.get(field_name)  # 关键字段名对应的值列表
            if not field_value_list:
                error_msg = f"fail:验证字段'{field_name}'不存在"
                break
            if len(tmp) == 1:  # 精确匹配第1个  ( name -> name )
                field_value = field_value_list[0]
                self.compare_core_field_name_list[i] = field_name
            elif len(tmp) == 2:  # 精确匹配第n个 或 条件匹配第1个
                num_condition = tmp[1]
                if num_condition in ["S", "E", "C"]:  # ( name::S -> name::S )
                    field_value = self.actual_field_dict.get(field_name)[0]
                    self.compare_core_field_name_list[i] = field
                elif num_condition.isdigit():  # ( name::3 -> name )
                    if int(num_condition) > len(field_value_list):
                        error_msg = "error:验证字段索引超出取值范围"
                        break
                    field_value = field_value_list[int(num_condition) - 1]
                    self.compare_core_field_name_list[i] = field_name
                else:
                    error_msg = "error:验证字段条件配置有误"
                    break
            else:  # 3 条件匹配第n个  ( name::3::S -> name::S )
                num, condition = tmp[1], tmp[2]
                if not num.isdigit() or condition not in ["S", "E", "C"]:
                    error_msg = "error:验证字段条件配置有误"
                    break
                if int(num) > len(field_value_list):
                    error_msg = "error:验证字段索引超出取值范围"
                    break
                field_value = field_value_list[int(num) - 1]
                self.compare_core_field_name_list[i] = f"{field_name}::{condition}"
            self.actual_core_field_value_list.append(field_value)
        return error_msg

    def verify_with_condition(self, error_msg):
        """
            比较'关键字段值'列表
            1.关于'httpRT'关键字的比较方式：实际毫秒数 <= 期望毫秒数，则 pass
            2.其余关键字的比较方式：按照匹配模式进行比较
            （1）精确匹配：name
            （2）匹配开始部分：name::S
            （3）匹配结尾部分：name::E
            （4）匹配任意部分：name::C
         """
        self.result_core_field_value = "pass"
        if error_msg:
            self.result_core_field_value = "fail" in error_msg and "fail" or "error"
        else:
            for i, field_name in enumerate(self.compare_core_field_name_list):
                if field_name == "httpRT":
                    if self.httpRT_actual != -1 and self.httpRT_actual > self.httpRT_expect:
                        self.result_core_field_value = "fail"
                        break
                else:
                    expect_value = str(self.expect_core_field_value_list[i]).strip()
                    actual_value = str(self.actual_core_field_value_list[i]).strip()
                    tmp = field_name.split("::")
                    if len(tmp) == 1:  # 精确匹配
                        if expect_value != actual_value:
                            self.result_core_field_value = "fail"
                            break
                    else:  # 条件匹配
                        condition = tmp[1]
                        if (condition == "S" and not actual_value.startswith(expect_value)) or \
                                (condition == "E" and not actual_value.endswith(expect_value)) or \
                                (condition == "C" and actual_value.find(expect_value) == -1):
                            self.result_core_field_value = "fail"
                            break

                for index, expect_value in enumerate(self.expect_core_field_value_list):
                    if self.httpRT_actual != -1 and index == self.httpRT_i:  # 响应时间 判断 是否小于等于
                        if self.httpRT_actual > self.httpRT_expect:
                            self.result_core_field_value = "fail"
                            break

    def inster_http_code_rt_data(self):
        """
        在'待比较关键字段列表、期望关键字段值列表、实际关键字段值列表'插入httpCode、httpRT相关数据（由于之前移除的缘故）
        """
        # 只有"httpCode"的情况
        if self.httpCode_i != -1 and self.httpRT_i == -1:
            self.compare_core_field_name_list.insert(self.httpCode_i, "httpCode")
            self.expect_core_field_value_list.insert(self.httpCode_i, self.httpCode_expect)
            self.actual_core_field_value_list.insert(self.httpCode_i, self.httpCode_actual)
        # 只有"httpRT"的情况
        if self.httpCode_i == -1 and self.httpRT_i != -1:
            self.compare_core_field_name_list.insert(self.httpRT_i, "httpRT")
            self.expect_core_field_value_list.insert(self.httpRT_i, self.httpRT_expect)
            self.actual_core_field_value_list.insert(self.httpRT_i, self.httpRT_actual)
        # "httpCode、httpRT"都存在的情况
        if self.httpCode_i != -1 and self.httpRT_i != -1:
            # 比较两个索引大小，先添加索引小的，再添加索引大的（ 确保添加的位置不会错乱 ）
            if self.httpCode_i < self.httpRT_i:
                self.compare_core_field_name_list.insert(self.httpCode_i, "httpCode")
                self.expect_core_field_value_list.insert(self.httpCode_i, self.httpCode_expect)
                self.actual_core_field_value_list.insert(self.httpCode_i, self.httpCode_actual)
                self.compare_core_field_name_list.insert(self.httpRT_i, "httpRT")
                self.expect_core_field_value_list.insert(self.httpRT_i, self.httpRT_expect)
                self.actual_core_field_value_list.insert(self.httpRT_i, self.httpRT_actual)
            else:
                self.compare_core_field_name_list.insert(self.httpRT_i, "httpRT")
                self.expect_core_field_value_list.insert(self.httpRT_i, self.httpRT_expect)
                self.actual_core_field_value_list.insert(self.httpRT_i, self.httpRT_actual)
                self.compare_core_field_name_list.insert(self.httpCode_i, "httpCode")
                self.expect_core_field_value_list.insert(self.httpCode_i, self.httpCode_expect)
                self.actual_core_field_value_list.insert(self.httpCode_i, self.httpCode_actual)

    def verify_response_field_name_list(self):
        """
        【 验证'待比较的响应字段列表' 】
        1.记录'不存在的字段名列表'
        2.若'不存在的字段名列表'有值，则记录结果为'fail'
          若'不存在的字段名列表'为空，则记录结果为'pass'
        :return:
        """
        not_exist_field_list = list(filter(lambda x: x not in self.actual_field_name_list, self.expect_field_name_list))
        self.result_field_name_list = not_exist_field_list and "fail" or "pass"

    def get_test_result(self):
        """
        【 获 取 测 试 结 果 】
        1.success:测试通过
        2.fail:关键字段验证失败
        3.fail:关键字段验证失败,响应字段列表验证失败
        4.fail:关键字段验证通过,响应字段列表验证失败
        5.fail:关键字段验证失败,响应字段列表验证通过
        :return:
        """
        if self.verify_mode == 1:
            self.test_result = self.result_core_field_value == "pass" and "success:测试通过" or "fail:关键字段验证失败"
        else:
            if self.result_core_field_value != "pass" and self.result_field_name_list != "pass":
                self.test_result = "fail:关键字段验证失败,响应字段列表验证失败"
            elif self.result_core_field_value == "pass" and self.result_field_name_list != "pass":
                self.test_result = "fail:关键字段验证通过,响应字段列表验证失败"
            elif self.result_core_field_value != "pass" and self.result_field_name_list == "pass":
                self.test_result = "fail:关键字段验证失败,响应字段列表验证通过"
            else:
                self.test_result = "success:测试通过"

    def get_mongo_update_result_dict(self):
        """
        【 获取 待数据库更新的字典 】
        """
        self.update_result_dict = {"response_info": self.response_info,
                                   "http_code": self.httpCode_actual,
                                   "http_rt": self.httpRT_actual,
                                   "actual_core_field_value_list": self.actual_core_field_value_list,
                                   "actual_field_name_list": self.actual_field_name_list,
                                   "downstream_relation_field_value_list": self.downstream_relation_field_value_list,
                                   "result_core_field_value": self.result_core_field_value,
                                   "result_field_name_list": self.result_field_name_list,
                                   "test_result": self.test_result}


if __name__ == "__main__":
    pass
    # http://127.0.0.1:7060/api_local/test/test_get_request?test_str=接口自动化测试&test_int=5&test_bool=True
    # http://127.0.0.1:7060/api_local/test/test_post_request   {"test_str":"post测试","test_int":5,"test_bool":"true"}

    # interface_name = "测试带参数的get请求"
    # interface_url = "http://127.0.0.1:7060/api_local/test/test_get_request"
    # request_method = "GET"
    # request_header = ""
    # request_params = "?test_str=接口自动化测试&test_int=5&test_bool=True"

    # interface_name = "测试post请求"
    # interface_url = "http://127.0.0.1:7060/api_local/test/test_post_request"
    # request_method = "POST"
    # request_header = "{\"Content-Type\": \"application/json\"}"
    # request_params = "{\"test_str\":\"post测试\",\"test_int\":5,\"test_bool\":\"true\"}"
    #
    # ri = VerifyInterface(interface_name=interface_name, interface_url=interface_url, request_method=request_method,
    #                       request_header=request_header, request_params=request_params)
    # respone = ri.send_request()
    # print(respone)
    # print(respone.status_code)
    # print(respone.text)
